var GownFullHTMLRichEditAdapter = GownFullObjectAdapter.extend({
	keydown_handler: null,
	keyup_handler: null,
	keypress_handler: null,
	focus_handler: null,
	lostsel_handler: null,

	is_focus: false,
	stored_selection: null,

	constructor: function(obj)
	{
		this.base(obj);
		//this.obj = obj;
	},
	clone: function(obj)
	{
		return new GownFullHTMLRichEditAdapter(obj);
	},
	GetContent: function()
	{
		var d = this.GetHTMLDocument();
		if(d) return d.innerHTML;
		else return null;
	},
	GetCursorXY: function()
	{
		var offset,pos;

		offset = JSVKBrowserExtension.getSelectionOffsetRichEdit(this.obj.contentWindow);
		pos = JSVKBrowserExtension.getOffset(this.obj);
		pos.x += offset.x;
		pos.y += offset.y;

		return new Array(pos.x,pos.y);
	},
	/*
           The code is copied from http://www.mozilla.org/editor/midasdemo/
        */
	MozillaInsertNodeAtSelection: function(win,insertNode)
	{
		// get current selection
		var sel = win.getSelection();
		
		// get the first range of the selection
		// (there's almost always only one range)
		var range = sel.getRangeAt(0);
		
		// deselect everything
		sel.removeAllRanges();
		
		// remove content of current selection from document
		range.deleteContents();
		
		// get location of current selection
		var container = range.startContainer;
		var pos = range.startOffset;
		
		// make a new range for the new selection
		range=document.createRange();
		
		if (container.nodeType==3 && insertNode.nodeType==3) {
		
			// if we insert text in a textnode, do optimized insertion
			container.insertData(pos, insertNode.nodeValue);
			
			// put cursor after inserted text
			range.setEnd(container, pos+insertNode.length);
			range.setStart(container, pos+insertNode.length);
		} else {
			var afterNode;
			if (container.nodeType==3) {
			
				// when inserting into a textnode
				// we create 2 new textnodes
				// and put the insertNode in between
			
				var textNode = container;
				container = textNode.parentNode;
				var text = textNode.nodeValue;
			
				// text before the split
				var textBefore = text.substr(0,pos);
				// text after the split
				var textAfter = text.substr(pos);
			
				var beforeNode = document.createTextNode(textBefore);
				afterNode = document.createTextNode(textAfter);
			
				// insert the 3 new nodes before the old one
				container.insertBefore(afterNode, textNode);
				container.insertBefore(insertNode, afterNode);
				container.insertBefore(beforeNode, insertNode);
			
				// remove the old node
				container.removeChild(textNode);
				// by wonghang
				range.setEnd(afterNode, 0);
				range.setStart(afterNode, 0);

			} else {
			
				// else simply insert the node
				afterNode = container.childNodes[pos];
				container.insertBefore(insertNode, afterNode);
				// by wonghang
				range.setEnd(insertNode, insertNode.length);
				range.setStart(insertNode, insertNode.length);

			}
/* original		
			range.setEnd(afterNode, 0);
			range.setStart(afterNode, 0);
*/
		}
		
		sel.addRange(range);
	},
	SendRawHTML: function(obj,html)
	{
		try {
			var doc;
			var ifme = this.obj;

			if(ifme.document) doc = ifme.document;
			else if(ifme.contentDocument) doc = ifme.contentDocument;

			doc.execCommand('insertHTML',false,html);

			Debug.trace(2,"GownFullHTMLRichEditAdapter: SendRawHTML: Using method 1");
			return true;
		}
		catch(e) {}
		try {
			var sel = null;

			if(obj.nodeName && obj.nodeName.toLowerCase() == 'iframe') {
				Debug.trace(2,"GownFullHTMLRichEditAdapter: SendRawHTML: Got iframe element, switch to window");
				obj = obj.contentWindow;
			}
			// try to set focus...
			if(obj.focus) obj.focus();
			// try to restore selection
			this.RestoreSelection(this.stored_selection);

			if(obj.getSelection) sel = obj.getSelection();
			else if(obj.document && obj.document.selection) sel = obj.document.selection;

			var rng = sel.createRange();
			rng.pasteHTML(html);
			rng.collapse(false);
			rng.select();
			Debug.trace(2,"GownFullHTMLRichEditAdapter: SendRawHTML: Using method 2 (%s, nodeName=%s)",obj,obj.nodeName);
			this.stored_selection = this.GetSelection();
			return true;
		}
		catch(e) {}

		Debug.trace(0,"GownFullHTMLRichEditAdapter: SendRawHTML: Failure to send HTML to iframe, maybe current browser is not supported.");
		return false;
	},
	SendString: function(o,str)
	{
		if(GownFullBrowserDetect.current_engine == GownFullBrowserDetect.ENGINE_GECKO) {
			var text = document.createTextNode(str);
			var win = this.obj.contentWindow;
			try { this.MozillaInsertNodeAtSelection(win,text); }
			catch(e) {}
			return true;
		}
		else {
			str = str.replace(/&/g,'&amp;');
			str = str.replace(/</g,'&lt;');
			str = str.replace(/>/g,'&gt;');
	
			return this.SendRawHTML(o,str);
		}
	},
	SendHTML: function(o,html)
	{
		return this.SendRawHTML(o,html);
	},
	isHTML: function()
	{
		return true;
	},
	SetFocus: function()
	{
		try {
			this.obj.contentWindow.focus();
		}
		catch(e) {}
	},
	AttachHandler: function(keydown_handler,keypress_handler,keyup_handler,focus_handler,lostsel_handler)
	{
		var x = this;

		if(keydown_handler) this.keydown_handler = keydown_handler;
		if(keyup_handler) this.keyup_handler = keyup_handler;
		if(keypress_handler) this.keypress_handler = keypress_handler;
		if(focus_handler) this.focus_handler = focus_handler;
		if(lostsel_handler) this.lostsel_handler = lostsel_handler;

		// special treatment for IE....
		if(GownFullBrowserDetect.current_browser == GownFullBrowserDetect.BROWSER_IE) {
			var win,doc;
			win = this.obj.contentWindow;
			doc = win.document;

			doc.GownFullObjectAdapter = this;
			if(doc.body) doc.body.GownFullObjectAdapter = this;

			// don't know why we cannot use attachEvent...
			doc.onkeydown = function() { return x.keydown_handler(win.event); }
			doc.onkeypress = function() {
				var b = x.keypress_handler(win.event); 
				if(b && x.is_focus) x.stored_selection = null; // invalidate selection if key is pressed
				return b;
			}
			doc.onkeyup = function() { return x.keyup_handler(win.event); }
/*
			this.obj.onfocus = function()
			{
				return x.focus_handler(window.event);
			}
*/
			doc.onselectionchange = function()
			{
				if(x.is_focus) x.stored_selection = x.GetSelection();
				return true;
			}
			this.obj.onfocus = function()
			{
				x.RestoreSelection(x.stored_selection);
				x.is_focus = true;
				x.stored_selection = x.GetSelection();
				return x.focus_handler(window.event);
			}
			this.obj.onblur = function()
			{
				x.is_focus = false;
				return true;
			}
			return true;
		}
		else {
			var doc;
			var obj = this.obj;

			if(obj.document) doc = obj.document;
			else if(obj.contentDocument) doc = obj.contentDocument;

			doc.GownFullObjectAdapter = this;
			if(doc.documentElement) doc.documentElement.GownFullObjectAdapter = this;

			obj = doc;
			if(GownFullBrowserDetect.use_key_addEventListener) {
				if(keydown_handler) obj.addEventListener("keydown", function(e) { return x.keydown_handler(e); },false);
				if(keypress_handler) obj.addEventListener("keypress", function(e) { return x.keypress_handler(e); },false);
				if(keyup_handler) obj.addEventListener("keyup", function(e) { return x.keyup_handler(e); },false);
				if(focus_handler) obj.addEventListener("focus",function(e) { return x.focus_handler(e); },false);
				if(lostsel_handler) obj.addEventListener("mousedown", function(e) { x.lostsel_handler(e); return true; },false);
			}
			else {
				if(keydown_handler) obj.onkeydown = function() { return x.keydown_handler(window.event); }
				if(keypress_handler) obj.onkeypress = function() { return x.keypress_handler(window.event); }
				if(keyup_handler) obj.onkeyup = function() { return x.keyup_handler(window.event); }
				if(focus_handler) obj.onfocus = function() { return x.focus_handler(window.event); }
				if(lostsel_handler) obj.onmousedown = function() { x.lostsel_handler(window.event); return true; }
			}
		}
	},
	GetHTMLDocument: function()
	{
		var o = this.obj;

		if(o.contentWindow) return o.contentWindow.document;
		else if(o.document) return o.document;
		else if(o.contentDocument) return o.contentDocument;
		else return null;
	},
	GetHTMLElement: function()
	{
		try {
			var sel,range,node;

			sel = this.obj.contentWindow.getSelection();
			range = sel.getRangeAt(0);
			node = range.startContainer;
			return node;
		}
		catch(e) {}
		Debug.trace(0,'GownFullHTMLRichEditAdapter: GetHTMLElement does not supported');
		return null;
	},
	GetHTMLElementById: function(id)
	{
		try {
			var doc;
			doc = this.obj.contentWindow.document;
			return doc.getElementById(id);
		}
		catch(e) {}
		Debug.trace(0,'GownFullHTMLRichEditAdapter: GetHTMLElementById does not supported');
		return null;
	},
	SetHTMLElementEventById: function(id,event,handler)
	{
		try {
			var doc;
			var obj;

			doc = this.obj.contentWindow.document;
			obj = doc.getElementById(id);
			
			if(GownFullBrowserDetect.use_addEventListener) {
				obj.addEventListener(event,handler,false);
			}
			else if(GownFullBrowserDetect.use_attachEvent) {
				obj.attachEvent("on" + event,handler);
			}
			else throw "Unsupported";
			return true;
		}
		catch(e) {}
		Debug.trace(0,'GownFullHTMLRichEditAdapter: SetHTMLElementEventById does not supported');
		return false;
	},
	GetSelection: function()
	{
		try { // should be happen only on IE
			var fwin = this.obj.contentWindow;
		
			return fwin.document.selection.createRange();
		}
		catch(e) {}
		return true;
	},
	RestoreSelection: function(sel)
	{
		try { // should be happen only on IE
			sel.select();
		}
		catch(e) {}
		return true;
	},
/* 
  The following codes are adopted from DocumentSelection library of JSVK

  Visit http://debugger.ru/demo/projects/virtualkeyboard/demo_inline.html for Virtual Keyboard

wonghang: updated to trunk 345. 2007-10-27
*/

	SetSelection: function(start,end,relative)
	{
		var ot = this.obj.scrollTop;
		var ol = this.obj.scrollLeft;

		if(relative) {
			var st = this.GetSelectionStart();
			end = st+end;
			start = st+start;
		}
		start = (start < 0) ? 0 : start;
		end = (end < start) ? start : end;

		var el = this.obj.contentWindow;

		Debug.trace(0,'start=%d, end=%d',start,end);
		if ('function' == typeof el.getSelection) {
			var sel = el.getSelection();
			sel.removeAllRanges();
			var r = el.document.createRange()
				,cnt = 0
				,cl = 0
				,cn
				,last = null
				,tw=document.createTreeWalker(el.document.body,NodeFilter.SHOW_TEXT,null,false);
			
			last = cn; // added by: wonghang, for storing the last TEXTNODE during the walk.
			while (cn=tw.nextNode()) {
				if (cn.nodeType != 3) continue;
				if (cnt+cn.nodeValue.length <= start) {
					cnt += cn.nodeValue.length;
				} else {
					r.setStart(cn,start-cnt);
					break;
				}
				last = cn;
			}
			if (cn) {
				do {
					if (cn.nodeType != 3) continue;
					if (cnt+cn.nodeValue.length < end) {
						cnt += cn.nodeValue.length;
					} else {
						r.setEnd(cn,end-cnt);
						break;
					}
				} while (cn=tw.nextNode())
			}
			else if(last) { 
				// added by: wonghang, cn may be null if the cursor is at the end of the RichEdit
				// we need to get the last TextNode and move the cursor to that node.
				r.setStart(last,last.nodeValue.length);
				r.setEnd(last,last.nodeValue.length);
				Debug.trace(0,'My Fix');
			}
			sel.addRange(r);
		} else if (el.document && el.document.selection) {
			el.document.body.setActive();
			var r = el.document.selection.createRange()
			r.moveToElementText(el.document.body);
			r.move("character",start);
			r.moveEnd("character",end-start);
			r.select();
		}
		this.keepScroll(ot,ol);
	},
	_GetSelectionPos: function(start)
	{
		var el = this.obj.contentWindow;
		var pos = 0;
		if ('function' == typeof el.getSelection) {
			/*
			*  we need to calculate both start and end points, because range could be reversed
			*  but we can't move selection end point before start one
			*/
			var sel = el.getSelection()
				,sn = sel.anchorNode
				,so = sel.anchorOffset
				,en = sel.focusNode
				,eo = sel.focusOffset
				,ss = false
				,es = false
				,sc = 0
				,ec = 0
				,cn
				,tw=document.createTreeWalker(el.document.body,NodeFilter.SHOW_TEXT,null,false)

			if (sn && sn.nodeType != 3) {
				sn = sn.childNodes[so]
				so = 0;
			}
			if (en && en.nodeType != 3) {
				en = en.childNodes[eo]
				eo = 0;
			}
			// added by: wonghang, see below
			var pcn = null, pen = null;

			while (cn=tw.nextNode()) {
				if (cn == en) {
					ec += eo;
					es = true;
				}
				if (cn == sn) {
					sc += so;
					ss = true;
				}
				// added by: wonghang
				// skip the last <br> node in Mozilla Firefox
				// There is a evil "\n" after the "dirty" <br> node and make the position calcuation wrong.
				pcn = cn.previousSibling;
				pen = en.previousSibling;
				if (pcn && pcn.nodeName.toLowerCase() == 'br' && 
				    (pcn.getAttribute('_moz_editor_bogus_node') == 'TRUE' || pcn.getAttribute('type') == '_moz')) break;

				if (pen && pen.nodeName.toLowerCase() == 'br' && 
				    (pen.getAttribute('_moz_editor_bogus_node') == 'TRUE' || pen.getAttribute('type') == '_moz')) break;

				if (!es) ec += cn.nodeValue.length;
				if (!ss) sc += cn.nodeValue.length;
				if (es && ss) break;
			}
			pos = start?Math.min(ec,sc):Math.max(ec,sc);
		} else {
			el.document.body.setActive();
			pos = Math.abs(el.document.selection.createRange()[start?"moveStart":"moveEnd"]("character", -100000000));
		}
		return pos;
	},
	GetSelectionStart: function() { return this._GetSelectionPos(true); },
	GetSelectionEnd: function() { return this._GetSelectionPos(false); },
	InsertAtCursor: function(str,keep)
	{
		var ot = this.obj.scrollTop;
		var ol = this.obj.scrollLeft;
		var el = this.obj.contentWindow;
		if(!keep) this.DeleteSelection();

		var p = this.GetSelectionStart() + str.length;
		if ('function' == typeof el.getSelection) {
			var n = el.document.createTextNode(str),s = el.getSelection();
			s.getRangeAt(0).insertNode(n);
			n.parentNode.normalize();
		} else if (el.document && el.document.selection) {
			this.obj.document.body.setActive();
			this.obj.document.selection.createRange().text = str;
		}
		this.SetSelection(p,p);
		this.keepScroll(ot,ol);
		return p;
	},
	DeleteAtCursor: function(after)
	{
		if(after) this.SetSelection(0,1,true);
		else this.SetSelection(-1,0,true);
		return this.DeleteSelection();
	},
	DeleteSelection: function() 
	{
		var ol = this.obj.scrollLeft;
		var ot = this.obj.scrollTop;
		var el = this.obj.contentWindow;

		if ('function' == typeof el.getSelection) {
			var s = el.getSelection()
				,i = s.rangeCount
			while (--i>-1) s.getRangeAt(i).deleteContents();
		} else if (el.document && el.document.selection) {
			el.document.selection.createRange().text = "";
	                el.document.selection.createRange().select(); // trunk 345: 2007-10-27 from jsvk
		}
		this.keepScroll(ot,ol);
	},
	keepScroll: function(ot,ol)
	{
		if (window.getSelection) {
			var q = this.GetCursorXY();
			var el = this.obj;
			if (el.contentWindow) el = el.contentWindow.document.body;
		
			if (ot>q[1])                      el.scrollTop = q[1];
			else if (ot+el.clientHeight>q[1]) el.scrollTop = ot;
			else                             el.scrollTop = q[1]-el.clientHeight/2; 
		
			if (ol>q[0])                      el.scrollLeft = q[0];
			else if (ol+el.clientWidth>q[0])  el.scrollLeft = ol;
			else                             el.scrollLeft = q[0]-el.clientWidth/2; 
		}
	}
});
